/*
 * GameController.java
 *
 * Created on January 11, 2007, 7:48 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package net.sf.iSnake.core;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.AbstractListModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.LineBorder;
import net.sf.iSnake.UI.GameFieldPanel;
import net.sf.iSnake.configuration.iSnakeConfiguration;
import net.sf.iSnake.server.ServerCorePre;

/**
 * Manages the overall operation of the game
 * @author thelinuxmaniac
 *
 * NOTE:    When adding player informations like (head,color,tails,etc) in a vector, the index for each player's value is one
 *          less than his player id, because the element index in a vector _MUST_ start from zero ( 0 )
 *      eg:
 *          if a player has player_id = 8, then he will find all his informations (like head,color,etc) in vectors with index (8-1) = 7
 *Jan 27, 2007 - Add the player details in vector without considering the index as adding and removing 
 *              players from the game will cause trouble
 *              So, just add the objjects without any index _FOR NOW_ only. Think of a clever method afterwards
 *
 */
public class GameController extends JPanel implements ActionListener    {
    
    private iSnakeConfiguration conf;           // Store the configuration for the iSnake Game Apllication
    
    private KeyboardInputHandler localPlayerKeyboardInputHandler;   // Keylistener class that will capture local keystrokes to control local player's snake    
    
    private ClientNetworkInterface clientNetworkInterface;          // for communications with the server
    
    private ISCP iscp;
    
    private ServerCorePre serverCorePre;            // The serverPreObject which handles all the game server operations
    
    private GameFieldPanel gameFieldPanel;      // Panel that hold the 64x48 cells each of 10x10 pixels known as GameField
    private JPanel GameStatusPanel;             // Panel that hold the game status like scores,lives,etc
    private JPanel GameMessagePanel;            // Panel that hold the game messages (like error msg,player joining/leaving game,etc)
    
    // for the functionality of chat messaging
    private JTextArea chatMessagesArea;
    private JTextField sendChatMessagesField;
    private boolean chatMessageArrived;
    private Integer messageLineNo;
    private Font chatMessageFont;
    private Color chatMessageColor;
    
    private JButton goBackButton;               // Button that allows to go back to the settings menu
    private JButton startGameButton;
    private JButton goForDebuging;              // Button that takes user to debuggomg window
    private JButton connectToServer;
    
    /**
     * For Message panel
     */
    private JTextArea messageTextArea;          // TextArea to dislay messages
    private boolean debug;                 // Level of debugging messages
    // debug levels
    public static Short DEBUG_LEVEL_MESSAGE = 1;
    public static Short DEBUG_LEVEL_WARNING = 2;
    public static Short DEBUG_LEVEL_ERROR = 3;
    public static Short DEBUG_LEVEL_DEVELOPER = 4;
    
    /**
     * For Status panel
     */
    private JPanel scoreAndLifePanel;
    private JPanel informationPanel;
    private JTabbedPane informationTabbedPane;
    private JList playerList;
    private JPanel playerInformationPanel;
    private JPanel playerChatPanel;
    
    private Short gameAreaMatrix[][];           // Representation of entire game status in matrix form [x][y]
    
    private Snake localSnake;                   // Snake object representing the local player snake
    private PlayerPreferences localPlayerPreferences;
    
    private GameAreaCoordinate currentFood;

    private static Vector gameSnakes = new Vector(10);                     // Stores the reference to the Snake object of different players
    private static Vector playersPreference = new Vector(10);              // Stores the preferences of remote players joining this game
    
    private static String panelName = "GameAreaPanel";              // Name of Panel used by Card Layout Manager
    
    private PositionValidator positionValidator;
    
    /**
     * GAME_OBJECT_PLAYER   =>  Reserved for player id (capability 1 to 99)
     *                          player_id = 1 is reserved for iSnake( hence from 11,12,... to 19 is reserved for iSnake in matrix)
     *
     * example: Suppose a player is assigned a player_id = 2
     * then the ID for this player's snake several parts is calculated using this formula
     *      snake_part_id = (player_id * 10) + part_INDEX
     *  eg:
     *      HEAD = 2*10+1   [ player_id = 2; part_INDEX = 1 (for head) ]
     *      BODY = 2*10+2   [ player_id = 2; part_INDEX = 2 (for body) ]
     *      TAIL = 2*10+3   [ player_id = 2; part_INDEX = 3 (for tail) ]
     *
     * To be more precise, the number space from (player_id*10) to (player_id*10+9) is reserved for player with id=player_id
     * In this case, for player with player_id=2, the number space from 20 to 29 is reserved for that player
     *
     */
    public final Short GAME_OBJECT_CURRENT_FOOD = 1000;         // Current Food
    public final Short GAME_OBJECT_NEXT_FOOD = 1001;            // Next Food
    public final Short GAME_OBJECT_WALL = 1100;                 // Wall
    public final Short GAME_OBJECT_NULL = 0;                    // Nothing - BLANK
    
    public static final Short GAME_AREA_MATRIX_WIDTH = 64;      // from 0 to 63 = total 64
    public static final Short GAME_AREA_MATRIX_HEIGHT = 48;     // from 0 to 47 = total 48
    
    public static final Short INDEX_SNAKE_HEAD = 1;             // 1 is for snake's head. eg: player_id = 2, then head = 2*10+1 = 21
    public static final Short INDEX_SNAKE_TAIL = 2;             // 2 is for snake's tail. eg: player_id = 2, then head = 2*10+2 = 22
    
    /*
     * Color setting for some game field objects
     */
    public static Color GAME_OBJECT_COLOR_WALL = Color.GRAY;        // Wall Color
    public static Color GAME_OBJECT_COLOR_CFOOD = Color.CYAN;       // Current food color
    public static Color GAME_OBJECT_COLOR_NFOOD = Color.MAGENTA;    // Next food color
    public static Color GAME_OBJECT_COLOR_NULL = Color.BLACK;       // Color for NOTHING :)
    
    private Short lastPlayerId;                         // Used by gameController when assigning player_id to a new player
    
    private postmodeISCPpacketForMulticast postmodeMulticastPacket;       // Stores the packets sent during postmode
    
     // for debugging and optimization purpose
    long startMS = 0;       // timer start count of millisecond
    long stopMS = 0;        // time stop count of millisecond
 
    private Long score;
    private boolean scoreChanged;
    private Font fontForScore;
    private JTextField scoreField;

    /** Creates a new instance of GameController */
    public GameController(iSnakeConfiguration conf, KeyboardInputHandler kh) {
        this.conf = conf;
        this.setLocalPlayerKeyboardInputHandler(kh);
    
        fontForScore = new Font(conf.ScoreFont,Font.BOLD,conf.ScoreFontSize);
        this.score = new Long("0");
        this.scoreChanged = true;
        
        setLayout(new BorderLayout());
        setBackground(conf.GameControllerBackgroundColor);
        setPreferredSize(conf.getDesiredFrameDimension());
    
        // Allocate memory for the GameArea Matrix
        gameAreaMatrix = new Short[GAME_AREA_MATRIX_HEIGHT][];
        for(int i=0;i<GAME_AREA_MATRIX_HEIGHT;i++)  {
            gameAreaMatrix[i] = new Short[GAME_AREA_MATRIX_WIDTH];
        }
        
        // initialize the gameField matrix to 0
        for(short i=0;i<GAME_AREA_MATRIX_WIDTH;i++)  {
            for(short j=0;j<GAME_AREA_MATRIX_HEIGHT;j++)   {
                setGameAreaMatrix(GAME_OBJECT_NULL,i,j);
            }
        }
        // Position validator for local player
        positionValidator = new PositionValidator();
        chatMessageFont = new Font(conf.ChatMessageAreaFont,Font.PLAIN,conf.ChatMessageAreaFontSize);
        
        /**
         * Initialize the gameFieldPanel
         */
        gameFieldPanel = new GameFieldPanel(conf,this,localPlayerKeyboardInputHandler);
        
        // Go Back button in the Game area which will take player to StandAloneModePanel
        goBackButton = conf.getPanelActionButtonForUIPanel(new JButton("back"));
        goBackButton.setMnemonic('A');
        goBackButton.setActionCommand("from_GameArea_to_*Mode");    // return panel is not know here :)
        
        // start game button
        setStartGameButton(conf.getPanelActionButtonForUIPanel(new JButton("start game")));
        getStartGameButton().setMnemonic('s');
        getStartGameButton().setActionCommand("from_GameArea_SIGNAL_start_game");
//        if(conf.getGameMode().equals(conf.GAME_MULTIPLAYER_CLIENT_MODE))    {
//            getStartGameButton().setVisible(false);
//        }
        getStartGameButton().addActionListener(this);
        
        // This test button is used for testing the GUI
        goForDebuging = conf.getPanelActionButtonForUIPanel(new JButton("debug"));
        goForDebuging.setMnemonic('d');
        goForDebuging.setActionCommand("from_GameArea_to_GameDebugPanel");

        // This button will connect to the server
        connectToServer = conf.getPanelActionButtonForUIPanel(new JButton("connect"));
        connectToServer.setMnemonic('c');
        connectToServer.setActionCommand("from_GameArea_ConnectToServer");
        connectToServer.addActionListener(this);
        
        // This button will disconnect to the server
        JButton disconnect = conf.getPanelActionButtonForUIPanel(new JButton("disconn"));
        disconnect.setMnemonic('i');
        disconnect.setActionCommand("from_GameArea_Disconnect");
        disconnect.addActionListener(this);

        GameStatusPanel = new JPanel(new GridLayout(2,1));
        GameStatusPanel.setBackground(Color.BLACK);
        GameStatusPanel.setPreferredSize(conf.GameStatusPanelDimension);
        
        scoreAndLifePanel = new JPanel(new BorderLayout());
        scoreAndLifePanel.setBackground(Color.BLACK);
        scoreAndLifePanel.setPreferredSize(conf.scoreAndLifePanelDimension);
        
        scoreField = conf.getTextFieldForUIPanel(new JTextField("0"));
        scoreField.setForeground(conf.ScoreColor);
        scoreField.setFont(fontForScore);
        scoreField.setEditable(false);
        scoreField.setBorder(new LineBorder(Color.BLACK,0));
        
        JPanel scorePanel = new JPanel(new GridLayout(4,1));
        scorePanel.setBackground(Color.BLACK);
        JLabel dummyLabel = new JLabel("");
        dummyLabel.setBackground(Color.BLACK);
        
        JLabel scoreLabel = conf.getLabelForUIPanel(new JLabel("Score"));
        
        scorePanel.add(dummyLabel);
        scorePanel.add(scoreLabel);
        scorePanel.add(scoreField);
        scorePanel.add(dummyLabel);
        scorePanel.add(dummyLabel);
        
        JPanel buttonsPanel = new JPanel(new GridLayout(3,2));
        buttonsPanel.setBackground(Color.BLACK);
        buttonsPanel.add(goForDebuging);
        buttonsPanel.add(goBackButton);
        buttonsPanel.add(connectToServer);
        buttonsPanel.add(disconnect);
        buttonsPanel.add(getStartGameButton());
        
        scoreAndLifePanel.add(buttonsPanel,BorderLayout.SOUTH);
        scoreAndLifePanel.add(scorePanel,BorderLayout.CENTER);
        
        
        GameStatusPanel.add(scoreAndLifePanel);
        
        informationPanel = new JPanel();
/*
        short id = 1;
        getPlayersPreference().add(new PlayerPreferences(id,"Test","Nepal","Kathmandu",Color.RED,0L,Calendar.getInstance()));
 */
        
        playerList = new JList(new PlayersListModel());
        playerList.setCellRenderer(new GradientSelectionRenderer());
        
        // chat functionality
        playerChatPanel = new JPanel(new BorderLayout());
        playerChatPanel.setVisible(false);
        setChatMessageArrived(false);       // initially no message is there
        setMessageLineNo(new Integer(0));
        setChatMessageColor(Color.BLACK);
        
        chatMessagesArea = new JTextArea("Chat Message Area");
        chatMessagesArea.setEditable(false);
        sendChatMessagesField = conf.getTextFieldForUIPanel(new JTextField(""));
        sendChatMessagesField.setBackground(Color.WHITE);
        sendChatMessagesField.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if(iscp != null)    {
                    if(iscp.isPremode())    {
                        System.out.println("Message Sending "+e.getActionCommand());
    //                    sendChatMessage(e.getActionCommand(),((PlayerPreferences) playerList.getSelectedValue()).getPlayer_id());
                          sendChatMessage(e.getActionCommand(),new Short("2"));
                    }
                }
                else    {
                    setMessageLineNo(getMessageLineNo());
                    chatMessagesArea.insert("Connect to server first !",getMessageLineNo());
                }
            }
        });
        
        JScrollPane scrollPane = new JScrollPane(chatMessagesArea);
        
        playerChatPanel.add(scrollPane,BorderLayout.CENTER);
        playerChatPanel.add(sendChatMessagesField,BorderLayout.SOUTH);        
        
        informationTabbedPane = new JTabbedPane(JTabbedPane.TOP);
        informationTabbedPane.add(playerList,"Player");        // Add the list containing the player list
        informationTabbedPane.add(playerChatPanel,"Chat");      // Add the chat panel for the local player
        
        informationPanel.add(informationTabbedPane);
        
        GameStatusPanel.add(informationTabbedPane);
        
        GameMessagePanel = new JPanel(new BorderLayout());
        GameMessagePanel.setBackground(Color.BLACK);
        GameMessagePanel.setPreferredSize(conf.GameMessagePanelDimension);
        messageTextArea = new JTextArea("iSnake v0.1b",6,80);
        messageTextArea.setLineWrap(true);
//      set the scroll panel to bottom of the test area
//        messageTextArea.set
        messageTextArea.setBackground(Color.WHITE);
        JScrollPane messageAreaScroll = new JScrollPane(messageTextArea);
        debug = true;       // set debugging mode
        GameMessagePanel.add(messageAreaScroll,BorderLayout.CENTER);

        add(GameStatusPanel,BorderLayout.EAST);
        add(GameMessagePanel,BorderLayout.SOUTH);
        add(gameFieldPanel,BorderLayout.CENTER);
    }

    /**
     * Captures the Button Clicks in the GameWindow and performs appropiate action as represented by ActionEvent
     */
    public void actionPerformed(ActionEvent e) {
        if(e.getActionCommand() == "from_GameArea_SIGNAL_start_game") {
            /**
             * For testing purpose only
             * implement requestFocus() functionality using KeyboardFocusManager
            gameFieldPanel.requestFocus();
            getLocalSnake().moveSnake(true);
            gameFieldPanel.updateGameField();
            getLocalSnake().addNewTail();
             */
            if(conf.getGameMode().equals(conf.GAME_MULTIPLAYER_SERVER_MODE))    {
                if(getServerCorePre().startGame())  {
                    status(DEBUG_LEVEL_MESSAGE,"Start game Signal sent to all remote players");
                }
                else    {
                    status(DEBUG_LEVEL_ERROR,"Could not send Start signal to remote players - ERR");
                }
            }
        }
        
        // command to connect to the server
        if(e.getActionCommand() == "from_GameArea_ConnectToServer") {
            // command = from_GameArea_ConnectToServer
//            System.out.println("from_GameArea_ConnectToServer");

            // get focus back to the game field
            gameFieldPanel.requestFocus();
            
            // Step 1. :Create an instance of ISCP for all the comminication encoding and decoding
            status(DEBUG_LEVEL_MESSAGE,"Connecting to server = ["+conf.getServerNameForClient()+"] on port: "+conf.getServerPortForClient());
            try {
                this.iscp = new ISCP(InetAddress.getByName(conf.getServerNameForClient()),conf.getServerPortForClient(),this,conf);
            } catch (UnknownHostException ex) {
                status(DEBUG_LEVEL_ERROR,"Unknow Host: "+conf.getServerNameForClient());
                ex.printStackTrace();
            }
            
            /**
             * Initialize objects
             */
            
            playerChatPanel.setVisible(true);
            postmodeMulticastPacket = new postmodeISCPpacketForMulticast();      // Initialize the object that will receive the post mode packets
            
            /***********************************************
             * Enter premode of the game
             ***********************************************/
            iscp.setPremode(true);
            iscp.setPostmode(false);
            
            // Wait until the server sends the plyaer_id
            status(DEBUG_LEVEL_MESSAGE,"Waiting for the player_id from server");
            
            /*
             * wait until the server does not send the player_id
             * @TODO this can cause the client to run into infinite loop when server is not running. FIX ME
             */
            while(iscp.getClientNetworkInterface().getClientNo() == -1);
            
            status(DEBUG_LEVEL_MESSAGE,"Received player_id = "+iscp.getClientNetworkInterface().getClientNo());
            
            // set the received player_id to localSnake
            getLocalSnake().setPlayer_id(iscp.getClientNetworkInterface().getClientNo());
            getLocalSnake().getPlayerPreferences().setPlayer_id(iscp.getClientNetworkInterface().getClientNo());

//            System.out.println("client no"+iscp.getClientNetworkInterface().getClientNo());
            
            setFocusable(true);
            
            // for testing purpose only
            //            PlayerPreferences pp_local = new PlayerPreferences(getLocalSnake().getPlayer_id(),"SATA","Nepal","Kathmandu",Color.GREEN,0L,Calendar.getInstance());
            //            PlayerPreferences pp_local = new PlayerPreferences(localSnake.getPlayer_id(),"TheLinuxManiac","Nepal","Kathmandu",Color.RED,0L,Calendar.getInstance());
            //            PlayerPreferences pp_local = new PlayerPreferences(localSnake.getPlayer_id(),"Implements","Nepal","Kathmandu",Color.BLUE,0L,Calendar.getInstance());
            
            // Step 3: send local player preference to the server
            status(DEBUG_LEVEL_MESSAGE,"Sending local player preferences for player_id: "+iscp.getClientNetworkInterface().getClientNo());
            
            // now send the player preferences
            iscp.PRE_encodeAndSendLocalPrefrence(getLocalPlayerPreferences());

            // update the local application player list to show the local player :)
            addGameSnakes(getLocalSnake());                             // adds reference to local player snake
            addPlayerPreference(getLocalPlayerPreferences());     // adds reference to local player preference
//            System.out.println("adding local player");

            
//            System.out.println("local player pref:"+localSnake.getPlayerPreferences().toString());
            // now pass on controls to the ISCP thread :)
            status(DEBUG_LEVEL_MESSAGE,"Now passing control to ISCP thread");
            iscp.start();
        }
        if(e.getActionCommand() == "from_GameArea_Disconnect") {
            try {
                getLocalPlayerKeyboardInputHandler().setStopKeyboardInputHandler(true);
                iscp.getClientNetworkInterface().getSoc().close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    /**
     * Updates the player list to contain the lastest list of all the connected player list
     */
    public void updateRemotePlayerList()  {
//        setPlayerPreferences(rpp);
        status(DEBUG_LEVEL_MESSAGE,"Updating Player's List");
//        System.out.println("Received player list: "+rpp.toString()+" size: "+rpp.size()+" hash= "+rpp.hashCode());
//        System.out.println("Local player list: "+getPlayersPreference().toString()+" size: "+getPlayersPreference().size()+" hash= "+getPlayersPreference().hashCode());
        // now update the player list in gui
        playerList.updateUI();
    }
    
    /**
     * Update the chat message area when text message arrives
     */
    public void updateChatPanel(String message,Short sender_id)   {
        String msgDisplay = getPlayerPreference(sender_id).getName()+">>"+message;
        setMessageLineNo(getMessageLineNo() + 1);
        chatMessagesArea.insert(msgDisplay, getMessageLineNo());
        setChatMessageArrived(false);
    }

    public void syncStep1() {
        // game starting
        // display "game starting" message
        status(DEBUG_LEVEL_MESSAGE,"syncStep1: Starting game ...");
    }
    
    /**
     * Changing of mode from PRE to POST invokes this method
     */
    public void syncStep2() {
        // ISCP is sleeping
        // now clientNetworkInterface will forward all the received packet to gameController
        sendChatMessagesField.setEditable(false);
        status(DEBUG_LEVEL_MESSAGE,"Entering post mode now");
    }
    
    /**
     * Updates the gameField in POST mode
     * ISCP thread is sleeping now
     * clientNetworkInterface now forwards all the post mode packets (including sync steps 3 and 4) to this method
     * @ obj postmodeISCPpacketForMulticast type object
     */

    public synchronized void updateGameFieldForPostMode(Object obj)
    {
        postmodeMulticastPacket = (postmodeISCPpacketForMulticast) obj;
        /*
         * Syncronization Step 3
         * All clients must have replied to step 2 by this time
         */
//        System.out.println("Postmode Command recived= "+postmodeMulticastPacket.getPostmodeMulticastCommand().toString());
        
        if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_MULTICAST_DXDY))   {
            // update local snakes to indicate the changes as in the received dx,dy
            // repaint the gameField after this
            // FOR BABU: send the dx dy properly mapped with it's player_id.
        /**    
            getLocalPlayerKeyboardInputHandler().setStartTrappingDirectionKeys(true);
            getLocalPlayerKeyboardInputHandler().setDxdyChanged(true);
         */
            startTimer();       // check timings
            getLocalPlayerKeyboardInputHandler().setSendKeystroke(true);
            getLocalPlayerKeyboardInputHandler().myNotifier();
            
//            System.out.println("postmodeMulticastPacket.getPlayersDxDy() length: "+postmodeMulticastPacket.getPlayersDxDy().length);
            for(int i=1;i<postmodeMulticastPacket.getPlayersDxDy().length+1;i++)  {
                /*
                 * ignore those players whose id is set to zero (0)
                 * game controller sets player_id to 0 for the players that leave the game during play
                 * it has been assumed that game server does not send dx dy for players who have already left the game
                 */
                int index = gameSnakes.indexOf(new Snake(i));
                if(index != -1) {

                    Short[][] dxdy = postmodeMulticastPacket.getPlayersDxDy();
                    Snake s = (Snake) gameSnakes.get(index);

                    System.out.println("old snake head at:"+s.getHead().toString());
                    System.out.println("dx="+dxdy[i][0]+", dy="+dxdy[i][1]);
                    // ignore all the players whose player_id has been set to 0 (player's who have left the game
                    if(!s.getPlayer_id().equals(new Short("0")))    {
//                        System.out.println("CLIENT INFO: Snake "+s.getPlayer_id()+" striked the wall. head was at: "+s.getHead().toString()+", start position= "+s.getSnakeStartPosition().toString()+"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
//                        System.out.println("postmodeMulticastPacket.isSnakeCollided()= "+postmodeMulticastPacket.isSnakeCollided()+" postmodeMulticastPacket.getCollidedPlayers().equals(s.getPlayer_id())= "+postmodeMulticastPacket.getCollidedPlayers().equals(s.getPlayer_id())+"////////////////////////////////////////////////////////////////////");
//                        System.out.println("collidedplayer length [ postmodeMulticastPacket.getCollidedPlayerCount() ]= "+postmodeMulticastPacket.getCollidedPlayerCount());
                        if(postmodeMulticastPacket.isSnakeCollided()){
                            System.out.println("Player Collided "+postmodeMulticastPacket.getCollidedPlayerCount());
                            if( postmodeMulticastPacket.isPlayerCollided(s.getPlayer_id())){
                            // reset the snake position to start pos. when collison with wall occurs
                            // server sends (dx=0,dy=0) when the snake collides to the wall
//                        System.out.println("postmodeMulticastPacket.isSnakeCollided()= "+postmodeMulticastPacket.isSnakeCollided()+" postmodeMulticastPacket.getCollidedPlayers().equals(s.getPlayer_id())= "+postmodeMulticastPacket.getCollidedPlayers().equals(s.getPlayer_id())+"////////////////////////////////////////////////////////////////////");
//                        System.out.println("collidedplayer length [ postmodeMulticastPacket.getCollidedPlayerCount() ]= "+postmodeMulticastPacket.getCollidedPlayerCount());

//                            System.out.println("CLIENT INFO: Snake "+s.getPlayer_id()+" striked the wall. head was at: "+s.getHead().toString()+", start position= "+s.getSnakeStartPosition().toString()+"\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
                            this.resetGameFieldMatrixContents(s.getHead());
                            s.setHead(s.getSnakeStartPosition());
                            s.resetSnakeTails();
                          }
                        }
                        s.setDxDy(dxdy[i][0],dxdy[i][1]);
                    }
                    s.moveSnake(true);     // write the changes to gameFieldMatrix and then update the graphics _NOW_
                }
            }
            // change this as required by game's performance

            gameFieldPanel.updateGameField();       // redraws the gameField matrix's graphical representation

            System.out.println(" Time taken to receive dx dy and update game field = "+stopTimer());
        }
        
        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_MULTICAST_FOOD_CONSUMED_BY))   {
            // contains the player_id who has consumed the food
            // update the food position in the gameFiled (contained in CURRENT_FOOD)
            status(DEBUG_LEVEL_MESSAGE,"Food was eaten by player: "+getPlayerPreference(postmodeMulticastPacket.getFoodConsumedBy()).getName());
            Snake s1 = (Snake) this.getGameSnake(postmodeMulticastPacket.getFoodConsumedBy());
            long newScore = s1.getScore() + new Integer(new Double(100*Math.random()).intValue()).longValue();
            s1.setScore(newScore);
            s1.addNewTail();
            this.scoreField.setText(new Long(getLocalSnake().getScore()).toString());
            this.scoreField.updateUI();
            // reset the old food position
            resetGameFieldMatrixContents(getCurrentFood());
            
            setCurrentFood(new GameAreaCoordinate(postmodeMulticastPacket.getCurrentFood()));
            
            for(Iterator itr = getGameSnakes().iterator();itr.hasNext();)    {
                Snake s = (Snake) itr.next();
                s.setCurrentFood(getCurrentFood());
            }
            setObjectToGameArea(GAME_OBJECT_CURRENT_FOOD,getCurrentFood());
            gameFieldPanel.updateGameField();       // redraws the gameField matrix's graphical representation
        }
        
        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_MULTICAST_CHANGE_LEVEL))   {
            // level has changed now
            // move to premode: set premode=true, postmode = false
            iscp.setPremode(true);
            iscp.setPostmode(false);
            System.out.println("Switching mode from POSTmode to PREmode");
            notifyAll();            // to wake up the sleeping iscp thread
        }
        
        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_MULTICAST_PLAYER_LEFT))   {
            // remove the corresponding player local snake object and clear it's presence from gamefield and the player list
            // remove all the traces of player_id from the client game
            Short playerLeft_id= new Short("0");
            for(Iterator itr = postmodeMulticastPacket.getPlayersLeft().iterator();itr.hasNext();)  {
                playerLeft_id = (Short) itr.next();
                status(DEBUG_LEVEL_MESSAGE,"Player: "+getPlayerName(playerLeft_id)+" left the game");
                // remove the tails for this player from the game field
                resetGameFieldMatrixContents(((Snake) this.getGameSnakes().get(playerLeft_id)).getTails());
                // remove the head for this player from the game field
                resetGameFieldMatrixContents(((Snake) this.getGameSnakes().get(playerLeft_id)).getHead());
                
                // now set the player_id for this snake to zero so that no further analysis of this player is done
                removeGameSnakes(getGameSnake(playerLeft_id));
                removePlayerPreference(playerLeft_id);
                gameFieldPanel.updateGameField();       // redraws the gameField matrix's graphical representation                
            }
        }

        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_SynchronizeClient))   {
            System.out.println("Step 7:");
            // all clients have now entered the post mode, including the game server
            status(DEBUG_LEVEL_MESSAGE,"Syncronizing with the server ...");
        }
        
        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_MULTICAST_CURRENT_FOOD))   {
            System.out.println("Step 8:");
            // store the first food coordinate sent by server for the coming game
            // now update the game field to show everything (snakes,walls,current food)
            // now server will only send "GAME START" pulse

            // reset the old food position
            
//            resetGameFieldMatrixContents(getCurrentFood());
            
            System.out.println("current food "+postmodeMulticastPacket.getCurrentFood().toString());
            try {
                setCurrentFood(postmodeMulticastPacket.getCurrentFood().clone());
            } catch (CloneNotSupportedException ex) {
                ex.printStackTrace();
            }
            
            for(Iterator itr = getGameSnakes().iterator();itr.hasNext();)    {
                Snake s = (Snake) itr.next();
                s.setCurrentFood(getCurrentFood());
            }
            setObjectToGameArea(GAME_OBJECT_CURRENT_FOOD,getCurrentFood());
            status(DEBUG_LEVEL_MESSAGE,"Game Started Successfully");
            gameFieldPanel.updateGameField();       // redraws the gameField matrix's graphical representation
        }
        
        else if(postmodeMulticastPacket.getPostmodeMulticastCommand().equals(ISCP.POST_CMD_GameStartPulse))   {
            System.out.println("Step 9:");
            // now start a new thread that will capture(verify) the direction keystrokes and continuously trasmit the
            // dx and dy through clientNetworkInterface.send() method to the gameServer
            // this transmission of keystrokes must only be done once while updating the gameField
            getLocalPlayerKeyboardInputHandler().setClientNetworkInterface(iscp.getClientNetworkInterface());

            getLocalPlayerKeyboardInputHandler().setStopKeyboardInputHandler(false);
            getLocalPlayerKeyboardInputHandler().setStartTrappingDirectionKeys(true);
            getLocalPlayerKeyboardInputHandler().setSendKeystroke(true);
            getLocalPlayerKeyboardInputHandler().setDxdyChanged(true);
            getLocalPlayerKeyboardInputHandler().setPlayer_id(getLocalSnake().getPlayer_id());
            getLocalPlayerKeyboardInputHandler().start();       // start the thread to capture and send keystrokes to server
            getLocalPlayerKeyboardInputHandler().myNotifier();

            gameFieldPanel.requestFocus();
        }
        // update Game field matrix which will update the GameField
    }
 
    /**
     * Creates the local snake object to represent the remote player's snake as new players join the game
     */
    public void addPlayersSnake(Vector rpp)
    {
//        System.out.println("remotePlayerPreferences: "+rpp);
        Snake s = new Snake(conf,this);
        for(Iterator itr = rpp.iterator();itr.hasNext();) {
            PlayerPreferences newPlayerPreference = (PlayerPreferences) itr.next();
            System.out.println("addPlayersSnake() received player_id: "+newPlayerPreference.getPlayer_id());
            // do not consider adding the player snakes that already exists in the vector
            if(!gameSnakes.contains(new Snake(newPlayerPreference.getPlayer_id()))) {
                s.setPlayerPreferences((PlayerPreferences) newPlayerPreference);
                // Local player snake and preferences has already been set
                /*
                 * Critical point. This can create problem as I am not sure about the clones
                 * DEBUG HERE
                 */
                System.out.println("adding local snake for player_id: "+newPlayerPreference.getPlayer_id());
                try {
                    Snake newSnake = s.clone();
                    newSnake.setPlayer_id(newPlayerPreference.getPlayer_id());
                    newSnake.setPlayerPreferences((PlayerPreferences) newPlayerPreference.clone());

                    addPlayerPreference(newSnake.getPlayerPreferences());  // adds the repective player preference reference                        
                    addGameSnakes(newSnake);           // adds the game snake to the local reference of game snakes
                } catch (CloneNotSupportedException ex) {
                    ex.printStackTrace();
                }           // adds the game snake to the local reference of game snakes


            }
        }
    }
    /**
     * Writes Wall into the gameFieldMatrix
     * @param xy contains the x and y coordinate for each block of the wall
     */
    
    public void writeWallToGameFieldMatrix(Vector wallCoordinates)
    {
        for(Iterator itr = wallCoordinates.iterator();itr.hasNext();)   {
            setObjectToGameArea(GAME_OBJECT_WALL,((GameAreaCoordinate) itr.next()));
        }
    }
    
    /**
     * Sets the starting position of all the game snakes (sent by server)
     */
    public void setStartPositionForSnakes(Vector snakeStartPos) {
        System.out.println("snake start send by babu: "+snakeStartPos.toString());
        int index = 0;
        System.out.println("gameSnakes size: "+gameSnakes.size());
        for(int i=0;i<snakeStartPos.size();i++)    {
            GameAreaCoordinate gac = (GameAreaCoordinate) snakeStartPos.get(i);
            if(!gac.equals(new GameAreaCoordinate(-1,-1)))  {
                index = gameSnakes.indexOf(new Snake(i));
                if(index != -1) {        // indexOf() returns -1 for the objects not found
                    System.out.println("player snakes id: "+((Snake) gameSnakes.get(index)).getPlayer_id());
                    try {
                        ((Snake) gameSnakes.get(index)).setHead((GameAreaCoordinate) gac.clone());
                        ((Snake) gameSnakes.get(index)).setSnakeStartPosition((GameAreaCoordinate) gac.clone()); // start position saved to reset the snake in future(wall collison)
                    } catch (CloneNotSupportedException ex) {
                        ex.printStackTrace();
                    }
                    ((Snake) gameSnakes.get(index)).writeSnakeToGameField();
//                    System.out.println("snake head for player_id= "+i+" "+((Snake) gameSnakes.get(index)).getHead().toString());
                }
                else
                    System.out.println("snake not found for index in head coordinates");
            }
            else    {
                System.out.println("ignoring (-1,-1) head coodinate");
            }
            
            // neglect (-1,-1) sent by babu :)
        }
    }
    
    /**
     * Updates the GameAreaMatrix contents as requested
     * @param gameFiledObjectId The unique id of the object which is to be updated (eg: gameFiledObjectId=1 for iSnake, gameFiledObjectId = 11 for iSnake HEAD, etc)
     * @param oldPositionNewValue The gameFiledObjectId which is to be set in oldCoordinate (which becomes vacant after updating the position)
     * @param newCoordinate The new coordinate to which the specified gameFiledObject is to be shifted
     */
    public boolean updateGameAreaMatrix(Short gameFieldObjectId, Short oldPositionNewValue, GameAreaCoordinate oldCoordinate, GameAreaCoordinate newCoordinate) {
        if(oldPositionNewValue < 0)
            oldPositionNewValue = 0;        // The value to be set at the position of oldCoordinate in the matrix after moving the object
        if(getObjectFromGameArea(oldCoordinate) == gameFieldObjectId)   {
            setObjectToGameArea(oldPositionNewValue,oldCoordinate);
            setObjectToGameArea(gameFieldObjectId,newCoordinate);
            return true;
        } else
            return false;
    }
    
    /**
     * Sets the value of GameAreaMatrix
     */
    private void setGameAreaMatrix(Short value, short x, short y) {
        /**
         * NOTE: As the memory allocation of 2D array takes place in the order
         * First: height(no of rows) of 2D matrix is specified (ie: the y-coordinate in our case)
         * Second: each row is then allocated specified no of columns (ie: the x-coordinate in our case)
         * Hence when setting the element we use
         * gameAreaMatrix[y][x] = value;    // and _NOT_ gameAreaMatrix[x][y] ...
         * However this matter should not be taken into consideration by users and they can supply the coordinates
         * in usual [x,y] fashion using the publicly available method
         */
        //System.out.println("setting x="+y+",y="+x+" to "+value);
        gameAreaMatrix[y][x] = value;
    }
    /**
     * Gets the gameFiledObject that is present at [x,y] in GameAreaMatrix
     */
    private Short getGameAreaMatrix(short x, short y) {
        /**
         * NOTE: As the memory allocation of 2D array takes place in the order
         * First: height(no of rows) of 2D matrix is specified (ie: the y-coordinate in our case)
         * Second: each row is then allocated specified no of columns (ie: the x-coordinate in our case)
         * Hence when setting the element we use
         * gameAreaMatrix[y][x] = value;    // and _NOT_ gameAreaMatrix[x][y] ...
         *
         * However this matter should not be taken into consideration by users and they can supply the coordinates
         * in usual [x,y] fashion using the publicly available method
         */
        return gameAreaMatrix[y][x];
    }
    
    /**
     * Returns the object that is associated with the specified GameAreaCoordinate
     * @param gac the coordinate whose content is to be retrived
     * @return the gameFiledObject associated with the given GameAreaCoordinate
     */
    public Short getObjectFromGameArea(GameAreaCoordinate gac) {
        if(gac.getX() >= 0 && gac.getY() >=0)   {
            if(gac.getX() < (GAME_AREA_MATRIX_WIDTH) && gac.getY() < (GAME_AREA_MATRIX_HEIGHT) )
                return getGameAreaMatrix(gac.getX(),gac.getY());
            else    {
                System.out.println("invalid game area coordinate received: "+gac.toString());
                return new Short("-1");      // Error - out of range
            }
        } else  {
            System.out.println("invalid game area coordinate received: "+gac.toString());
            return new Short("-1");      // Error - out of range
        }
    }
    
    /**
     * Sets the object that is associated with the specified GameAreaCoordinate to given gameFieldObject
     * @param gac the coordinate whose content is to be set to new one
     * @param gameFiledObjectId The object that is to be placed in the new position
     * @return the gameFiledObject associated with the given GameAreaCoordinate after changing it
     */
    public Short setObjectToGameArea(Short gameFiledObjectId, GameAreaCoordinate gac) {
        if(gac.getX() >= 0 && gac.getY() >=0)   {
            if(gac.getX() < (GAME_AREA_MATRIX_WIDTH) && gac.getY() < (GAME_AREA_MATRIX_HEIGHT) ) {
                setGameAreaMatrix(gameFiledObjectId,gac.getX(),gac.getY());
                return gameFiledObjectId;
            } else  {
                System.out.println("invalid game area content received: "+gameFiledObjectId);
                return new Short("-1");      // Error - out of range
            }
        } else  {
            System.out.println("invalid game area content received: "+gameFiledObjectId);
            return new Short("-1");      // Error - out of range
        }
            
    }
    
    /**
     * It resets the contents of GameFieldMatrix (ie: removes the trail of snake) when the snake moves over the Field
     * @param gac The coordinate representing the cell of gameFieldMatrix which is to be reset
     */
    public void resetGameFieldMatrixContents(GameAreaCoordinate gac) {
        setObjectToGameArea(this.GAME_OBJECT_NULL,gac);
    }
    
    /**
     * It resets the contents for Game Field Objects like Walls which is represented by a collection of coordinates
     * @param v A Vector containing all the coordinates that is required to represent the game field object which is to be cleared
     */
    public void resetGameFieldMatrixContents(Vector v) {
        GameAreaCoordinate gac = new GameAreaCoordinate();
        
        for(Iterator itr = v.iterator();itr.hasNext();)  {
            gac.set((GameAreaCoordinate) itr.next());
            resetGameFieldMatrixContents(gac);
        }
    }

    public static String getPanelName() {
        return panelName;
    }
    
    public static void setPanelName(String aPanelName) {
        panelName = aPanelName;
    }
    
    public JButton getGoBackButton() {
        return goBackButton;
    }
    
    public void setGoBackButton(JButton goBackButton) {
        this.goBackButton = goBackButton;
    }
    
    public GameFieldPanel getGameFieldPanel() {
        return gameFieldPanel;
    }
    
    public void setGameFieldPanel(GameFieldPanel gameFieldPanel) {
        this.gameFieldPanel = gameFieldPanel;
    }
    
    
    /**
     * For testing of GUI
     */
    public void insertTestValuesToMatrix() {
        // sets a horizontal wall from (10,10) to (10,14)
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(0,0));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,0));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(0,1));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,1));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,2));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,3));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,4));
        setObjectToGameArea(this.GAME_OBJECT_WALL,new GameAreaCoordinate(1,5));
        
        // sets current food at (20,40)
        setObjectToGameArea(this.GAME_OBJECT_CURRENT_FOOD,new GameAreaCoordinate(3,3));
        
        // sets next food at (30,40)
        setObjectToGameArea(this.GAME_OBJECT_NEXT_FOOD,new GameAreaCoordinate(62,46));
    }
    
    public JButton getGoForDebuging() {
        return goForDebuging;
    }
    
    public void setGoForDebuging(JButton goForDebuging) {
        this.goForDebuging = goForDebuging;
    }
    
    /**
     * Returns the 2D matrix which stores all the game field status in it
     */
    public Short[][] getGameAreaMatrix() {
        return gameAreaMatrix;
    }
    
 

    /**
     * Adds a new snake object to the list of registered snakes
     * @param s snake object that is to be registered    
     */
    public void addGameSnakes(Snake s) {
        this.getGameSnakes().add(s);
    }
    
    /**
     * Adds a new player preference object to the list of game snakes  preferences
     * @param pp the desired player preference to be added
     */
    public void addPlayerPreference(PlayerPreferences pp)    {
//        System.out.println("inside addPlayerPreference(): size="+getPlayersPreference().size()+" player_id="+pp.getPlayer_id());
        this.getPlayersPreference().add(pp);  // map player_id to vector's index for fast retrival
    }
    
    /**
     * When the client receives a vector containing all the remote players(including local player), it will make a copy of it and store for it's local reference
     */
    public void setPlayerPreferences(Vector pp) {
        this.playersPreference = (Vector) pp.clone();
    }
    
    /**
     * Removes player preferences from the list (used when players leave the game
     * NOTE: removing a player preference from game means setting it's player id to zero
     * @param player_id the id of player who left the game
     */
    public boolean removePlayerPreference(Short player_id) {
        // removing a player means setting it's player_id to zero
        int index = this.getPlayersPreference().indexOf(new PlayerPreferences(player_id));
        if(index != -1) {
            ((PlayerPreferences) this.getPlayersPreference().get(index)).setPlayer_id(0);
            return true;
        }
        else
            return false;
    }
    
    /**
     * Removes a snake from the list of gameSnakes
     * NOTE: removing a player from game means setting it's player id to zero
     * @param player_id the player snake id which is to be removed
     */
    public boolean removeSnake(Short player_id)    {
        // removing a snake means setting it's player_id to zero
        int index = this.getPlayersPreference().indexOf(new PlayerPreferences(player_id));
        if(index != -1) {
            ((Snake) this.getGameSnakes().get(index)).setPlayer_id(0);    
            return true;
        }
        else
            return false;        
    }
    
    /**
     * Removes the snake object from the list of registered (active) snakes
     * @param s Snake object to be removed
     */
    public boolean removeGameSnakes(Snake s)   {
        return this.getGameSnakes().remove(s);
    }
    
    /**
     * To display messages in the Status Message Panel
     * @param debug_level the debug level of the message. available constants are DEBUG_LEVEL_ERROR,DEBUG_LEVEL_WARNING,....
     * @param message The message to be displayed
     */
    public void status(Short debug_level,String message)    {
        if(debug_level == this.DEBUG_LEVEL_ERROR)   {
            messageTextArea.append("\n"+Calendar.getInstance().getTime().toString()+" : "+message);
            
            return;
        }
        if(debug)   {
            messageTextArea.append("\n"+Calendar.getInstance().getTime().toString()+" : "+message);
            return;
        }
    }

    /**
     * Returns a player name matching the player_id from the vector playersPerference
     * @param player_id The requested player's id
     * @return Player's name
     */

    public String getPlayerName(Short player_id)   {
        return ((PlayerPreferences) playersPreference.get(player_id.intValue())).getName();
    }
    
    /**
     * Returns PlayerPreference matching the player_id
     * @param player_id Id of the interested player
     * @return PlayerPreferences object
     */
    
    public PlayerPreferences getPlayerPreference(Short player_id)    {
        int index = playersPreference.indexOf(new PlayerPreferences(player_id));
        if(index != -1)
            return ((PlayerPreferences) playersPreference.get(index));
        else
            return new PlayerPreferences(0);
    }
    
    public Color getPlayerColor(String playerName)    {
        PlayerPreferences pp = new PlayerPreferences();
        
        for(Iterator itr = playersPreference.iterator();itr.hasNext();) {
            pp = (PlayerPreferences) itr.next();
            if(pp.getName().equals(playerName)) {
                return pp.getSnakeColor();
            }
        }
        return Color.BLACK;
    }

    /**
     * Returns the snake object referred by the argument player_id
     * @param player_id Player id whose snake object is to be accessed
     */
    public Snake getGameSnake(Short player_id) {
        int index = gameSnakes.indexOf(new Snake(player_id));
        if(index != -1)
            return (Snake) gameSnakes.get(index);
        else
            return new Snake(0);        // not found
    }

   public Snake getLocalSnake() {
        return localSnake;
    }
    
    public void setLocalSnake(Snake localSnake) {
        this.localSnake = localSnake;
    }
    
    /**
     * To send chat message to remote players when the game is in PRE mode
     * Chat messaging is disabled in POST mode of the game
     * @param message The message to be sent to the remote player
     * @receiver_id The player id for the remote player for whom is message is sent
     */
    void sendChatMessage(String message,Short receiver_id)    {
        premodeISCPpacket chatPacket = new premodeISCPpacket(ISCP.PRE_CMD_ChatMessage);
        chatPacket.setChatMessage(message);
        chatPacket.setChatMessageSenderId(getLocalSnake().getPlayer_id());
        chatPacket.setChatMessageReceiverId(receiver_id);

        iscp.getClientNetworkInterface().send(chatPacket);
        System.out.println("Message '"+message+"' sent to "+receiver_id);
    }

    class PlayersListModel extends AbstractListModel{
        public int getSize() {
            return getPlayersPreference().size();
        }

        public Object getElementAt(int index) {
            String playerName = ((PlayerPreferences) getPlayersPreference().get(index)).getName();
            return playerName;
        }
        
    }
    
    class GradientSelectionRenderer extends DefaultListCellRenderer {
        private boolean drawGradient = false;
        
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus)    {
            JLabel label = (JLabel) super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
            drawGradient = false;
            label.setOpaque(false);
            Color playerColor = getPlayerColor((String) value);
            label.setForeground(playerColor);
            label.setBackground(Color.WHITE);
 
            if(isSelected)  {
                drawGradient = true;
                label.setForeground(Color.BLACK);
                label.setBackground(playerColor);
                // @TODO change this
//                label.setBackground(Color.GREEN);   // does not work
//                label.setOpaque(true);
            }
            repaint();
            return label;
        }
        
        public void paintComponents(Graphics g) {
            if(drawGradient)    {
                Graphics2D g2 = (Graphics2D) g;
                GradientPaint gradient = new GradientPaint(0,0,Color.BLUE,getWidth(),getHeight(),Color.RED);
                g2.setPaint(gradient);
                g2.fillRect(0,0,getWidth(),getHeight());
                g2.dispose();
            }
            super.paintComponent(g);
        }
    }

    public Vector getPlayersPreference() {
        return playersPreference;
    }

    public ServerCorePre getServerCorePre() {
        return serverCorePre;
    }

    public void setServerCorePre(ServerCorePre serverCorePre) {
        this.serverCorePre = serverCorePre;
    }

    public PlayerPreferences getLocalPlayerPreferences() {
        return localPlayerPreferences;
    }

    public Vector getGameSnakes() {
        return gameSnakes;
    }

    public void setGameSnakes(Vector gameSnakes) {
        this.gameSnakes = gameSnakes;
    }

    public GameAreaCoordinate getCurrentFood() {
        return currentFood;
    }

    public void setCurrentFood(GameAreaCoordinate currentFood) {
        this.currentFood = currentFood;
    }

    public KeyboardInputHandler getLocalPlayerKeyboardInputHandler() {
        return localPlayerKeyboardInputHandler;
    }

    public void setLocalPlayerKeyboardInputHandler(KeyboardInputHandler localPlayerKeyboardInputHandler) {
        this.localPlayerKeyboardInputHandler = localPlayerKeyboardInputHandler;
    }

    public JButton getStartGameButton() {
        return startGameButton;
    }

    public void setStartGameButton(JButton startGameButton) {
        this.startGameButton = startGameButton;
    }

    public void setLocalPlayerPreferences(PlayerPreferences localPlayerPreferences) {
        this.localPlayerPreferences = localPlayerPreferences;
    }

    public boolean isChatMessageArrived() {
        return chatMessageArrived;
    }

    public void setChatMessageArrived(boolean chatMessageArrived) {
        this.chatMessageArrived = chatMessageArrived;
    }

    public Integer getMessageLineNo() {
        return messageLineNo;
    }

    public void setMessageLineNo(Integer messageLineNo) {
        this.messageLineNo = messageLineNo;
    }
    
    /**
     * For debugging and optimization purpose
     */
    public void startTimer()    {
        this.startMS = Calendar.getInstance().getInstance().getTimeInMillis();
    }
    
    public long stopTimer()  {
        this.stopMS = Calendar.getInstance().getInstance().getTimeInMillis();
        return (this.stopMS - this.startMS);
    }

    public Color getChatMessageColor() {
        return chatMessageColor;
    }

    public void setChatMessageColor(Color chatMessageColor) {
        this.chatMessageColor = chatMessageColor;
    }
}